home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / app / resize.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-02-12  |  44.8 KB  |  1,410 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. #include "config.h"
  20.  
  21. #include <gtk/gtk.h>
  22.  
  23. #include "apptypes.h"
  24.  
  25. #include "appenv.h"
  26. #include "gdisplay.h"
  27. #include "resize.h"
  28. #include "undo.h"
  29. #include "gimprc.h"
  30. #include "gimpui.h"
  31.  
  32. #include "libgimp/gimpchainbutton.h"
  33. #include "libgimp/gimplimits.h"
  34. #include "libgimp/gimpmath.h"
  35. #include "libgimp/gimpsizeentry.h"
  36.  
  37. #include "libgimp/gimpintl.h"
  38.  
  39. #define EVENT_MASK        GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK
  40. #define DRAWING_AREA_SIZE 200
  41. #define TEXT_WIDTH        35
  42.  
  43. typedef struct _ResizePrivate ResizePrivate;
  44.  
  45. struct _ResizePrivate
  46. {
  47.   /*  size frame  */
  48.   GtkWidget *orig_width_label;
  49.   GtkWidget *orig_height_label;
  50.  
  51.   GtkWidget *size_se;
  52.  
  53.   GtkObject *ratio_x_adj;
  54.   GtkObject *ratio_y_adj;
  55.   GtkWidget *constrain;
  56.  
  57.   /*  offset frame  */
  58.   GtkWidget *offset_se;
  59.   GtkWidget *drawing_area;
  60.  
  61.   /*  resolution frame  */
  62.   GtkWidget *printsize_se;
  63.   GtkWidget *resolution_se;
  64.   GtkWidget *equal_res;
  65.  
  66.   gdouble ratio;
  67.   gint    old_width, old_height;
  68.   gdouble old_res_x, old_res_y;
  69.   gint    area_width, area_height;
  70.   gint    start_x, start_y;
  71.   gint    orig_x, orig_y;
  72. };
  73.  
  74. static void  resize_draw                 (Resize    *resize);
  75. static void  unit_update                 (GtkWidget *widget,
  76.                       gpointer   data);
  77. static gint  resize_bound_off_x          (Resize    *resize,
  78.                       gint       off_x);
  79. static gint  resize_bound_off_y          (Resize    *resize,
  80.                       gint       off_y);
  81. static void  orig_labels_update          (GtkWidget *widget,
  82.                       gpointer   data);
  83. static void  reset_callback              (GtkWidget *widget,
  84.                       gpointer   data);
  85. static void  size_callback               (GtkWidget *widget,
  86.                       gpointer   data);
  87. static void  ratio_callback              (GtkWidget *widget,
  88.                       gpointer   data);
  89. static void  size_update                 (Resize    *widget,
  90.                       gdouble    width,
  91.                       gdouble    height,
  92.                       gdouble    ratio_x,
  93.                       gdouble    ratio_y);
  94. static void  offset_update               (GtkWidget *widget,
  95.                       gpointer   data);
  96. static gint  resize_events               (GtkWidget *widget,
  97.                       GdkEvent  *event);
  98. static void  printsize_update            (GtkWidget *widget,
  99.                       gpointer   data);
  100. static void  resolution_callback         (GtkWidget *widget,
  101.                       gpointer   data);
  102. static void  resolution_update           (Resize    *resize,
  103.                       gdouble    res_x,
  104.                       gdouble    res_y);
  105. static void  resize_scale_warn_callback  (GtkWidget *widget,
  106.                       gboolean   do_scale,
  107.                       gpointer   data);
  108.  
  109.  
  110. Resize *
  111. resize_widget_new (ResizeType    type,
  112.            ResizeTarget  target,
  113.            GtkObject    *object,
  114.            gchar        *signal,
  115.            gint          width,
  116.            gint          height,
  117.            gdouble       resolution_x,
  118.            gdouble       resolution_y,
  119.            GimpUnit      unit,
  120.            gboolean      dot_for_dot,
  121.            GtkSignalFunc ok_cb,
  122.            GtkSignalFunc cancel_cb,
  123.            gpointer      user_data)
  124. {
  125.   Resize *resize;
  126.   ResizePrivate *private;
  127.   GtkWidget *main_vbox;
  128.   GtkWidget *vbox;
  129.   GtkWidget *table;
  130.   GtkWidget *table2;
  131.   GtkWidget *label;
  132.   GtkWidget *frame;
  133.   GtkWidget *spinbutton;
  134.   GtkWidget *abox;
  135.   GtkObject *adjustment;
  136.  
  137.   abox = NULL;
  138.   frame = NULL;
  139.  
  140.   private = g_new0 (ResizePrivate, 1);
  141.   private->old_width  = width;
  142.   private->old_height = height;
  143.   private->old_res_x  = resolution_x;
  144.   private->old_res_y  = resolution_y;
  145.  
  146.   resize = g_new (Resize, 1);
  147.   resize->type         = type;
  148.   resize->target       = target;
  149.   resize->private_part = private;
  150.   resize->width        = width;
  151.   resize->height       = height;
  152.   resize->resolution_x = resolution_x;
  153.   resize->resolution_y = resolution_y;
  154.   resize->unit         = unit;
  155.   resize->ratio_x      = 1.0;
  156.   resize->ratio_y      = 1.0;
  157.   resize->offset_x     = 0;
  158.   resize->offset_y     = 0;
  159.  
  160.   /*  Get the image width and height variables, based on the gimage  */
  161.   if (width > height)
  162.     private->ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) width;
  163.   else
  164.     private->ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) height;
  165.  
  166.   private->area_width  = (gint) (private->ratio * width);
  167.   private->area_height = (gint) (private->ratio * height);
  168.  
  169.   /*  dialog box  */
  170.   {
  171.     const gchar *wmclass = NULL;
  172.     const gchar *window_title = NULL;
  173.     gchar *help_page = NULL;
  174.  
  175.     switch (type)
  176.       {
  177.       case ScaleWidget:
  178.     switch (target)
  179.       {
  180.       case ResizeLayer:
  181.         wmclass = "scale_layer";
  182.         window_title = _("Scale Layer");
  183.         help_page = "layers/dialogs/scale_layer.html";
  184.         frame = gtk_frame_new (_("Size"));
  185.         break;
  186.       case ResizeImage:
  187.         wmclass = "scale_image";
  188.         window_title = _("Scale Image");
  189.         help_page = "dialogs/scale_image.html";
  190.         frame = gtk_frame_new (_("Pixel Dimensions"));
  191.         break;
  192.       }
  193.     break;
  194.  
  195.       case ResizeWidget:
  196.     switch (target)
  197.       {
  198.       case ResizeLayer:
  199.         wmclass = "resize_layer";
  200.         window_title = _("Set Layer Boundary Size");
  201.         help_page = "layers/dialogs/layer_boundary_size.html";
  202.         break;
  203.       case ResizeImage:
  204.         wmclass = "resize_image";
  205.         window_title = _("Set Canvas Size");
  206.         help_page = "dialogs/set_canvas_size.html";
  207.         break;
  208.       }
  209.     frame = gtk_frame_new (_("Size"));
  210.     break;
  211.       }    
  212.  
  213.     resize->resize_shell =
  214.       gimp_dialog_new (window_title, wmclass,
  215.                gimp_standard_help_func, help_page,
  216.                GTK_WIN_POS_MOUSE,
  217.                FALSE, FALSE, TRUE,
  218.  
  219.                _("OK"), ok_cb,
  220.                user_data, NULL, NULL, TRUE, FALSE,
  221.  
  222.                _("Reset"), reset_callback,
  223.                resize, NULL, NULL, FALSE, FALSE,
  224.  
  225.                _("Cancel"), cancel_cb ? cancel_cb : gtk_widget_destroy,
  226.                cancel_cb ? user_data : NULL,
  227.                cancel_cb ? NULL : (gpointer) 1,
  228.                NULL, FALSE, TRUE,
  229.  
  230.                NULL);
  231.  
  232.     gtk_signal_connect_object (GTK_OBJECT (resize->resize_shell), "destroy",
  233.                    GTK_SIGNAL_FUNC (g_free),
  234.                    (GtkObject *) private);
  235.     gtk_signal_connect_object (GTK_OBJECT (resize->resize_shell), "destroy",
  236.                    GTK_SIGNAL_FUNC (g_free),
  237.                    (GtkObject *) resize);
  238.   }
  239.  
  240.   /*  handle the image disappearing under our feet  */
  241.   if (object && signal)
  242.     {
  243.       if (cancel_cb)
  244.     gtk_signal_connect (GTK_OBJECT (object), signal,
  245.                 cancel_cb,
  246.                 user_data);
  247.       else
  248.     gtk_signal_connect_object_while_alive
  249.       (GTK_OBJECT (object), signal,
  250.        GTK_SIGNAL_FUNC (gtk_widget_destroy),
  251.        GTK_OBJECT (resize->resize_shell));
  252.     }
  253.  
  254.   /*  the main vbox  */
  255.   main_vbox = gtk_vbox_new (FALSE, 4);
  256.   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 4);
  257.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (resize->resize_shell)->vbox),
  258.              main_vbox);
  259.  
  260.   /*  the pixel dimensions frame  */
  261.   gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  262.   gtk_widget_show (frame);
  263.  
  264.   vbox = gtk_vbox_new (FALSE, 2);
  265.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  266.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  267.  
  268.   table = gtk_table_new (6, 2, FALSE);
  269.   gtk_container_set_border_width (GTK_CONTAINER (table), 2);
  270.   gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4);
  271.   gtk_table_set_row_spacing (GTK_TABLE (table), 0, 4);
  272.   gtk_table_set_row_spacing (GTK_TABLE (table), 1, 4);
  273.   gtk_table_set_row_spacing (GTK_TABLE (table), 3, 4);
  274.   gtk_table_set_row_spacing (GTK_TABLE (table), 4, 2);
  275.   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  276.  
  277.   /*  the original width & height labels  */
  278.   label = gtk_label_new (_("Original Width:"));
  279.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  280.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
  281.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  282.   gtk_widget_show (label);
  283.  
  284.   label = gtk_label_new (_("Height:"));
  285.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  286.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
  287.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  288.   gtk_widget_show (label);
  289.  
  290.   private->orig_width_label = gtk_label_new ("");
  291.   gtk_misc_set_alignment (GTK_MISC ( private->orig_width_label), 0.0, 0.5);
  292.   gtk_table_attach (GTK_TABLE (table),  private->orig_width_label, 1, 2, 0, 1,
  293.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  294.   gtk_widget_show ( private->orig_width_label);
  295.  
  296.   private->orig_height_label = gtk_label_new ("");
  297.   gtk_misc_set_alignment (GTK_MISC (private->orig_height_label), 0.0, 0.5);
  298.   gtk_table_attach (GTK_TABLE (table), private->orig_height_label, 1, 2, 1, 2,
  299.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  300.   gtk_widget_show (private->orig_height_label);
  301.  
  302.   /*  the new size labels  */
  303.   label = gtk_label_new (_("New Width:"));
  304.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  305.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
  306.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  307.   gtk_widget_show (label);
  308.  
  309.   label = gtk_label_new (_("Height:"));
  310.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  311.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
  312.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  313.   gtk_widget_show (label);
  314.  
  315.   /*  the new size sizeentry  */
  316.   adjustment = gtk_adjustment_new (1, 1, 1, 1, 10, 1);
  317.   spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 2);
  318.   gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
  319.                                    GTK_SHADOW_NONE);
  320.   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  321.   gtk_widget_set_usize (spinbutton, 75, 0);
  322.  
  323.   private->size_se =
  324.     gimp_size_entry_new (1, unit, "%a", TRUE, TRUE, FALSE, 75,
  325.              GIMP_SIZE_ENTRY_UPDATE_SIZE);
  326.   gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->size_se),
  327.                  GTK_SPIN_BUTTON (spinbutton), NULL);
  328.   gtk_table_attach_defaults (GTK_TABLE (private->size_se), spinbutton,
  329.                  1, 2, 0, 1);
  330.   gtk_widget_show (spinbutton);
  331.   gtk_table_attach (GTK_TABLE (table), private->size_se, 1, 2, 2, 4,
  332.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  333.   gtk_widget_show (private->size_se);
  334.  
  335.   if (dot_for_dot)
  336.     gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->size_se),
  337.                   GIMP_UNIT_PIXEL);
  338.  
  339.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se), 0,
  340.                   resolution_x, FALSE);
  341.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se), 1,
  342.                   resolution_y, FALSE);
  343.  
  344.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->size_se), 0,
  345.                      GIMP_MIN_IMAGE_SIZE,
  346.                      GIMP_MAX_IMAGE_SIZE);
  347.   gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (private->size_se), 1,
  348.                      GIMP_MIN_IMAGE_SIZE,
  349.                      GIMP_MAX_IMAGE_SIZE);
  350.  
  351.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->size_se), 0, 0, width);
  352.   gimp_size_entry_set_size (GIMP_SIZE_ENTRY (private->size_se), 1, 0, height);
  353.  
  354.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se), 0, width);
  355.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se), 1, height);
  356.  
  357.   gtk_signal_connect (GTK_OBJECT (private->size_se), "value_changed",
  358.               GTK_SIGNAL_FUNC (size_callback),
  359.               resize);
  360.   gtk_signal_connect (GTK_OBJECT (private->size_se), "unit_changed",
  361.               GTK_SIGNAL_FUNC (orig_labels_update),
  362.               resize);
  363.  
  364.   /*  initialize the original width & height labels  */
  365.   orig_labels_update (private->size_se, resize);
  366.  
  367.   /*  the scale ratio labels  */
  368.   label = gtk_label_new (_("Ratio X:"));
  369.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  370.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
  371.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  372.   gtk_widget_show (label);
  373.  
  374.   label = gtk_label_new (_("Y:"));
  375.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  376.   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6,
  377.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  378.   gtk_widget_show (label);
  379.  
  380.   /*  a table for the spinbuttons and the chainbutton  */
  381.   abox = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
  382.   table2 = gtk_table_new (2, 2, FALSE);
  383.   gtk_table_set_col_spacing (GTK_TABLE (table2), 0, 2);
  384.   gtk_table_set_row_spacing (GTK_TABLE (table2), 0, 2);
  385.   gtk_container_add (GTK_CONTAINER (abox), table2);
  386.   gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 4, 6,
  387.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  388.   gtk_widget_show (abox);
  389.   
  390.   /*  the scale ratio spinbuttons  */
  391.   private->ratio_x_adj =
  392.     gtk_adjustment_new (resize->ratio_x, 
  393.             (double) GIMP_MIN_IMAGE_SIZE / (double) resize->width,
  394.             (double) GIMP_MAX_IMAGE_SIZE / (double) resize->width,
  395.             0.01, 0.1, 1);
  396.   spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (private->ratio_x_adj),
  397.                                     0.01, 4);
  398.   gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
  399.                                    GTK_SHADOW_NONE);
  400.   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  401.   gtk_widget_set_usize (spinbutton, 75, 0);
  402.   gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 0, 1);
  403.   gtk_signal_connect (GTK_OBJECT ( private->ratio_x_adj), "value_changed",
  404.               (GtkSignalFunc) ratio_callback,
  405.               resize);
  406.   gtk_widget_show (spinbutton);
  407.  
  408.   private->ratio_y_adj =
  409.     gtk_adjustment_new (resize->ratio_y,
  410.             (double) GIMP_MIN_IMAGE_SIZE / (double) resize->height,
  411.             (double) GIMP_MAX_IMAGE_SIZE / (double) resize->height,
  412.             0.01, 0.1, 1);
  413.   spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (private->ratio_y_adj),
  414.                                     0.01, 4);
  415.   gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
  416.                                    GTK_SHADOW_NONE);
  417.   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  418.   gtk_widget_set_usize (spinbutton, 75, 0);
  419.   gtk_table_attach_defaults (GTK_TABLE (table2), spinbutton, 0, 1, 1, 2);
  420.   gtk_signal_connect (GTK_OBJECT ( private->ratio_y_adj), "value_changed",
  421.               (GtkSignalFunc) ratio_callback,
  422.               resize);
  423.   gtk_widget_show (spinbutton);
  424.  
  425.   /*  the constrain ratio chainbutton  */
  426.   private->constrain = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
  427.   gimp_chain_button_set_active (GIMP_CHAIN_BUTTON (private->constrain), TRUE);
  428.   gtk_table_attach_defaults (GTK_TABLE (table2), private->constrain, 1, 2, 0, 2);
  429.   gtk_widget_show (private->constrain);
  430.  
  431.   gtk_widget_show (table2);
  432.   gtk_widget_show (table);
  433.   gtk_widget_show (vbox);
  434.  
  435.   /*  the offset frame  */
  436.   if (type == ResizeWidget)
  437.     {
  438.       frame = gtk_frame_new (_("Offset"));
  439.       gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  440.       gtk_widget_show (frame);
  441.  
  442.       vbox = gtk_vbox_new (FALSE, 4);
  443.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  444.       gtk_container_add (GTK_CONTAINER (frame), vbox);
  445.  
  446.       abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  447.       gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
  448.  
  449.       /*  the offset sizeentry  */
  450.       adjustment = gtk_adjustment_new (1, 1, 1, 1, 10, 1);
  451.       spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 2);
  452.       gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
  453.                        GTK_SHADOW_NONE);
  454.       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  455.       gtk_widget_set_usize (spinbutton, 75, 0);
  456.  
  457.       private->offset_se =
  458.     gimp_size_entry_new (1, unit, "%a", TRUE, FALSE, FALSE, 75,
  459.                  GIMP_SIZE_ENTRY_UPDATE_SIZE);
  460.       gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->offset_se),
  461.                  GTK_SPIN_BUTTON (spinbutton), NULL);
  462.       gtk_table_attach_defaults (GTK_TABLE (private->offset_se), spinbutton,
  463.                  1, 2, 0, 1);
  464.       gtk_table_set_col_spacing (GTK_TABLE (private->offset_se), 0, 4);
  465.       gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (private->offset_se),
  466.                     _("X:"), 0, 0, 1.0);
  467.       gimp_size_entry_attach_label (GIMP_SIZE_ENTRY (private->offset_se),
  468.                     _("Y:"), 1, 0, 1.0);
  469.       gtk_widget_show (spinbutton);
  470.       gtk_container_add (GTK_CONTAINER (abox), private->offset_se);
  471.       gtk_widget_show (private->offset_se);
  472.  
  473.       if (dot_for_dot)
  474.     gimp_size_entry_set_unit (GIMP_SIZE_ENTRY (private->offset_se),
  475.                   GIMP_UNIT_PIXEL);
  476.  
  477.       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->offset_se), 0,
  478.                       resolution_x, FALSE);
  479.       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->offset_se), 1,
  480.                       resolution_y, FALSE);
  481.  
  482.       gimp_size_entry_set_refval_boundaries
  483.     (GIMP_SIZE_ENTRY (private->offset_se), 0, 0, 0);
  484.       gimp_size_entry_set_refval_boundaries
  485.     (GIMP_SIZE_ENTRY (private->offset_se), 1, 0, 0);
  486.  
  487.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 0, 0);
  488.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se), 1, 0);
  489.  
  490.       gtk_signal_connect (GTK_OBJECT (private->offset_se), "value_changed",
  491.               GTK_SIGNAL_FUNC (offset_update),
  492.               resize);
  493.  
  494.       gtk_widget_show (abox);
  495.  
  496.       /*  frame to hold drawing area  */
  497.       abox = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  498.       gtk_box_pack_start (GTK_BOX (vbox), abox, FALSE, FALSE, 0);
  499.  
  500.       frame = gtk_frame_new (NULL);
  501.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  502.       gtk_container_add (GTK_CONTAINER (abox), frame);
  503.  
  504.       private->drawing_area = gtk_drawing_area_new ();
  505.       gtk_drawing_area_size (GTK_DRAWING_AREA (private->drawing_area),
  506.                  private->area_width, private->area_height);
  507.       gtk_widget_set_events (private->drawing_area, EVENT_MASK);
  508.       gtk_signal_connect (GTK_OBJECT (private->drawing_area), "event",
  509.               GTK_SIGNAL_FUNC (resize_events),
  510.               NULL);
  511.       gtk_object_set_user_data (GTK_OBJECT (private->drawing_area), resize);
  512.       gtk_container_add (GTK_CONTAINER (frame), private->drawing_area);
  513.       gtk_widget_show (private->drawing_area);
  514.       gtk_widget_show (frame);
  515.  
  516.       gtk_widget_show (abox);
  517.       gtk_widget_show (vbox);
  518.     }
  519.  
  520.   /*  the resolution stuff  */
  521.   if ((type == ScaleWidget) && (target == ResizeImage))
  522.     {
  523.       frame = gtk_frame_new (_("Print Size & Display Unit"));
  524.       gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
  525.       gtk_widget_show (frame);
  526.  
  527.       vbox = gtk_vbox_new (FALSE, 2);
  528.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  529.       gtk_container_add (GTK_CONTAINER (frame), vbox);
  530.  
  531.       table = gtk_table_new (4, 2, FALSE);
  532.       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 4);
  533.       gtk_table_set_row_spacing (GTK_TABLE (table), 1, 4);
  534.       gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
  535.  
  536.       /*  the print size labels  */
  537.       label = gtk_label_new (_("New Width:"));
  538.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  539.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
  540.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  541.       gtk_widget_show (label);
  542.  
  543.       label = gtk_label_new (_("Height:"));
  544.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  545.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
  546.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  547.       gtk_widget_show (label);
  548.  
  549.       /*  the print size sizeentry  */
  550.       abox = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
  551.       adjustment = gtk_adjustment_new (1, 1, 1, 1, 10, 1);
  552.       spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 2);
  553.       gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
  554.                        GTK_SHADOW_NONE);
  555.       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  556.       gtk_widget_set_usize (spinbutton, 75, 0);
  557.       gtk_container_add (GTK_CONTAINER (abox), spinbutton);
  558.       gtk_widget_show (spinbutton);
  559.       gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 0, 1,
  560.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  561.       gtk_widget_show (abox);
  562.  
  563.       abox = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
  564.       private->printsize_se =
  565.     gimp_size_entry_new (1, unit, "%a", FALSE, FALSE, FALSE, 75,
  566.                  GIMP_SIZE_ENTRY_UPDATE_SIZE);
  567.       gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->printsize_se),
  568.                  GTK_SPIN_BUTTON (spinbutton), NULL);
  569.       gtk_container_add (GTK_CONTAINER (abox), private->printsize_se);
  570.       gtk_table_attach (GTK_TABLE (table), abox, 1, 2, 1, 2,
  571.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  572.       gtk_widget_show (private->printsize_se);
  573.       gtk_widget_show (abox);
  574.  
  575.       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->printsize_se),
  576.                       0, resolution_x, FALSE);
  577.       gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->printsize_se),
  578.                       1, resolution_y, FALSE);
  579.  
  580.       gimp_size_entry_set_refval_boundaries
  581.     (GIMP_SIZE_ENTRY (private->printsize_se),
  582.      0, GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE);
  583.       gimp_size_entry_set_refval_boundaries
  584.     (GIMP_SIZE_ENTRY (private->printsize_se),
  585.      1, GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE);
  586.  
  587.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->printsize_se),
  588.                   0, resize->width);
  589.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->printsize_se),
  590.                   1, resize->height);
  591.  
  592.       gtk_signal_connect (GTK_OBJECT (private->printsize_se), "value_changed",
  593.               GTK_SIGNAL_FUNC (printsize_update),
  594.               resize);
  595.       gtk_signal_connect (GTK_OBJECT (private->printsize_se), "unit_changed",
  596.               GTK_SIGNAL_FUNC (unit_update),
  597.               resize);
  598.       
  599.       /*  the resolution labels  */
  600.       label = gtk_label_new (_("Resolution X:"));
  601.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  602.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
  603.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  604.       gtk_widget_show (label);
  605.  
  606.       label = gtk_label_new (_("Y:"));
  607.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  608.       gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
  609.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  610.       gtk_widget_show (label);
  611.  
  612.       /*  the resolution sizeentry  */
  613.       adjustment = gtk_adjustment_new (1, 1, 1, 1, 10, 1);
  614.       spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 2);
  615.       gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton),
  616.                        GTK_SHADOW_NONE);
  617.       gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
  618.       gtk_widget_set_usize (spinbutton, 75, 0);
  619.  
  620.       private->resolution_se =
  621.     gimp_size_entry_new (1, default_resolution_units, _("pixels/%a"),
  622.                  FALSE, FALSE, FALSE, 75,
  623.                  GIMP_SIZE_ENTRY_UPDATE_RESOLUTION);
  624.       gtk_table_set_col_spacing (GTK_TABLE (private->resolution_se), 1, 2);
  625.       gtk_table_set_col_spacing (GTK_TABLE (private->resolution_se), 2, 2);
  626.       gimp_size_entry_add_field (GIMP_SIZE_ENTRY (private->resolution_se),
  627.                  GTK_SPIN_BUTTON (spinbutton), NULL);
  628.       gtk_table_attach_defaults (GTK_TABLE (private->resolution_se), spinbutton,
  629.                  1, 2, 0, 1);
  630.       gtk_widget_show (spinbutton);
  631.       gtk_table_attach (GTK_TABLE (table), private->resolution_se, 1, 2, 2, 4,
  632.             GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
  633.       gtk_widget_show (private->resolution_se);
  634.  
  635.       gimp_size_entry_set_refval_boundaries
  636.     (GIMP_SIZE_ENTRY (private->resolution_se),
  637.      0, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION);
  638.       gimp_size_entry_set_refval_boundaries
  639.     (GIMP_SIZE_ENTRY (private->resolution_se),
  640.      1, GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION);
  641.  
  642.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->resolution_se),
  643.                   0, resize->resolution_x);
  644.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->resolution_se),
  645.                   1, resize->resolution_y);
  646.  
  647.       gtk_signal_connect (GTK_OBJECT (private->resolution_se), "value_changed",
  648.               GTK_SIGNAL_FUNC (resolution_callback),
  649.               resize);
  650.  
  651.       /*  the resolution chainbutton  */
  652.       private->equal_res = gimp_chain_button_new (GIMP_CHAIN_RIGHT);
  653.       gimp_chain_button_set_active
  654.     (GIMP_CHAIN_BUTTON (private->equal_res),
  655.      ABS (resize->resolution_x - resize->resolution_y) < 1e-5);
  656.       gtk_table_attach_defaults (GTK_TABLE (private->resolution_se),
  657.                  private->equal_res, 2, 3, 0, 2);
  658.       gtk_widget_show (private->equal_res);
  659.  
  660.       gtk_widget_show (table);
  661.       gtk_widget_show (vbox);
  662.     }
  663.  
  664.   gtk_widget_show (main_vbox);
  665.  
  666.   /*  finally, activate the first entry  */
  667.   gimp_size_entry_grab_focus (GIMP_SIZE_ENTRY (private->size_se));
  668.  
  669.   return resize;
  670. }
  671.  
  672. static void
  673. resize_draw (Resize *resize)
  674. {
  675.   GtkWidget *widget;
  676.   ResizePrivate *private;
  677.   gint aw, ah;
  678.   gint x, y;
  679.   gint w, h;
  680.  
  681.   /*  Only need to draw if it's a resize widget  */
  682.   if (resize->type != ResizeWidget)
  683.     return;
  684.  
  685.   private = (ResizePrivate *) resize->private_part;
  686.   widget = private->drawing_area;
  687.  
  688.   /*  If we're making the size larger  */
  689.   if (private->old_width <= resize->width)
  690.     w = resize->width;
  691.   /*  otherwise, if we're making the size smaller  */
  692.   else
  693.     w = private->old_width * 2 - resize->width;
  694.   /*  If we're making the size larger  */
  695.   if (private->old_height <= resize->height)
  696.     h = resize->height;
  697.   /*  otherwise, if we're making the size smaller  */
  698.   else
  699.     h = private->old_height * 2 - resize->height;
  700.  
  701.   if (w > h)
  702.     private->ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) w;
  703.   else
  704.     private->ratio = (gdouble) DRAWING_AREA_SIZE / (gdouble) h;
  705.  
  706.   aw = (gint) (private->ratio * w);
  707.   ah = (gint) (private->ratio * h);
  708.  
  709.   if (aw != private->area_width || ah != private->area_height)
  710.     {
  711.       private->area_width  = aw;
  712.       private->area_height = ah;
  713.       gtk_drawing_area_size (GTK_DRAWING_AREA (private->drawing_area), aw, ah);
  714.     }
  715.  
  716.   if (private->old_width <= resize->width)
  717.     x = private->ratio * resize->offset_x;
  718.   else
  719.     x = private->ratio * (resize->offset_x + private->old_width - resize->width);
  720.   if (private->old_height <= resize->height)
  721.     y = private->ratio * resize->offset_y;
  722.   else
  723.     y = private->ratio * (resize->offset_y + private->old_height - resize->height);
  724.  
  725.   w = private->ratio * private->old_width;
  726.   h = private->ratio * private->old_height;
  727.  
  728.   gdk_window_clear (private->drawing_area->window);
  729.   gtk_draw_shadow (widget->style, widget->window,
  730.            GTK_STATE_NORMAL, GTK_SHADOW_OUT,
  731.            x, y, w, h);
  732.  
  733.   /*  If we're making the size smaller  */
  734.   if (private->old_width > resize->width ||
  735.       private->old_height > resize->height)
  736.     {
  737.       if (private->old_width > resize->width)
  738.     {
  739.       x = private->ratio * (private->old_width - resize->width);
  740.       w = private->ratio * resize->width;
  741.     }
  742.       else
  743.     {
  744.       x = -1;
  745.       w = aw + 2;
  746.     }
  747.       if (private->old_height > resize->height)
  748.     {
  749.       y = private->ratio * (private->old_height - resize->height);
  750.       h = private->ratio * resize->height;
  751.     }
  752.       else
  753.     {
  754.       y = -1;
  755.       h = ah + 2;
  756.     }
  757.  
  758.       gdk_draw_rectangle (private->drawing_area->window,
  759.               widget->style->black_gc, 0,
  760.               x, y, w, h);
  761.     }
  762. }
  763.  
  764. static gint
  765. resize_bound_off_x (Resize *resize,
  766.             gint    off_x)
  767. {
  768.   ResizePrivate *private;
  769.  
  770.   private = (ResizePrivate *) resize->private_part;
  771.  
  772.   if (private->old_width <= resize->width)
  773.     off_x = CLAMP (off_x, 0, (resize->width - private->old_width));
  774.   else
  775.     off_x = CLAMP (off_x, (resize->width - private->old_width), 0);
  776.  
  777.   return off_x;
  778. }
  779.  
  780. static gint
  781. resize_bound_off_y (Resize *resize,
  782.             gint    off_y)
  783. {
  784.   ResizePrivate *private;
  785.  
  786.   private = (ResizePrivate *) resize->private_part;
  787.  
  788.   if (private->old_height <= resize->height)
  789.     off_y = CLAMP (off_y, 0, (resize->height - private->old_height));
  790.   else
  791.     off_y = CLAMP (off_y, (resize->height - private->old_height), 0);
  792.  
  793.   return off_y;
  794. }
  795.  
  796. static void
  797. orig_labels_update (GtkWidget *widget,
  798.             gpointer   data)
  799. {
  800.   Resize        *resize;
  801.   ResizePrivate *private;
  802.   GimpUnit       unit;
  803.   gchar          format_buf[16];
  804.   gchar          buf[32];
  805.  
  806.   static GimpUnit label_unit = GIMP_UNIT_PIXEL;
  807.  
  808.   resize = (Resize *) data;
  809.   private = (ResizePrivate *) resize->private_part;
  810.  
  811.   unit = gimp_size_entry_get_unit (GIMP_SIZE_ENTRY (widget));
  812.  
  813.   if (unit != GIMP_UNIT_PERCENT)
  814.     label_unit = unit;
  815.  
  816.   if (label_unit) /* unit != GIMP_UNIT_PIXEL */
  817.     {
  818.       gdouble unit_factor = gimp_unit_get_factor (label_unit);
  819.   
  820.       g_snprintf (format_buf, sizeof (format_buf), "%%.%df %s",
  821.                   gimp_unit_get_digits (label_unit) + 1,
  822.                   gimp_unit_get_symbol (label_unit));
  823.       g_snprintf (buf, sizeof (buf), format_buf,
  824.                   private->old_width * unit_factor / private->old_res_x);
  825.       gtk_label_set_text (GTK_LABEL (private->orig_width_label), buf);
  826.       g_snprintf (buf, sizeof (buf), format_buf,
  827.                   private->old_height * unit_factor / private->old_res_y);
  828.       gtk_label_set_text (GTK_LABEL (private->orig_height_label), buf);
  829.     }
  830.   else /* unit == GIMP_UNIT_PIXEL */
  831.     {
  832.       g_snprintf (buf, sizeof (buf), "%d", private->old_width);
  833.       gtk_label_set_text (GTK_LABEL (private->orig_width_label), buf);
  834.       g_snprintf (buf, sizeof (buf), "%d", private->old_height);
  835.       gtk_label_set_text (GTK_LABEL (private->orig_height_label), buf);
  836.     }
  837. }
  838.  
  839. static void
  840. unit_update (GtkWidget *widget,
  841.          gpointer   data)
  842. {
  843.   Resize *resize;
  844.  
  845.   resize = (Resize *) data;
  846.   resize->unit = gimp_size_entry_get_unit (GIMP_SIZE_ENTRY (widget));
  847. }
  848.  
  849. static void
  850. offset_update (GtkWidget *widget,
  851.            gpointer   data)
  852. {
  853.   Resize *resize;
  854.   ResizePrivate *private;
  855.   gint offset_x;
  856.   gint offset_y;
  857.  
  858.   resize = (Resize *) data;
  859.   private = (ResizePrivate *) resize->private_part;
  860.  
  861.   offset_x =
  862.     RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->offset_se), 0));
  863.   offset_x = resize_bound_off_x (resize, offset_x);
  864.  
  865.   offset_y =
  866.     RINT (gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->offset_se), 1));
  867.   offset_y = resize_bound_off_y (resize, offset_y);
  868.  
  869.   if ((offset_x != resize->offset_x) ||
  870.       (offset_y != resize->offset_y))
  871.     {
  872.       resize->offset_x = offset_x;
  873.       resize->offset_y = offset_y;
  874.       resize_draw (resize);
  875.     }
  876. }
  877.  
  878. /*
  879.  * Callback function for "Reset" button.
  880.  * Data must be a pointer pointer to a Resize structure.
  881.  */
  882. static void
  883. reset_callback (GtkWidget *widget,
  884.                 gpointer data)
  885. {
  886.   Resize *resize;
  887.   ResizePrivate *private;
  888.  
  889.   resize = (Resize *)data;
  890.   private = (ResizePrivate *)resize->private_part;
  891.  
  892.   /* restore size and ratio settings */
  893.   size_update (resize, private->old_width, private->old_height, 1.0, 1.0);
  894.  
  895.   if ((resize->type == ScaleWidget) && (resize->target == ResizeImage))
  896.     {
  897.       /* restore resolution settings */
  898.       resolution_update (resize, private->old_res_x, private->old_res_y);
  899.     }
  900.  
  901. }
  902.   
  903. static void
  904. size_callback (GtkWidget *widget,
  905.            gpointer   data)
  906. {
  907.   Resize *resize;
  908.   ResizePrivate *private;
  909.   gdouble width;
  910.   gdouble height;
  911.   gdouble ratio_x;
  912.   gdouble ratio_y;
  913.  
  914.   resize  = (Resize *) data;
  915.   private = (ResizePrivate *) resize->private_part;
  916.  
  917.   width  = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), 0);
  918.   height = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), 1);
  919.  
  920.   ratio_x = width  / (gdouble) private->old_width;
  921.   ratio_y = height / (gdouble) private->old_height;
  922.  
  923.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (private->constrain)))
  924.     {
  925.       if (ratio_x != resize->ratio_x)
  926.     {
  927.       ratio_y = ratio_x;
  928.       height = (gdouble) private->old_height * ratio_y;
  929.       height = CLAMP (height, GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE);
  930.     }
  931.       else
  932.     {
  933.       ratio_x = ratio_y;
  934.       width = (gdouble) private->old_width * ratio_x;
  935.       width = CLAMP (width, GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE);
  936.     }
  937.     }
  938.  
  939.   size_update (resize, width, height, ratio_x, ratio_y);
  940. }
  941.  
  942. static void
  943. ratio_callback (GtkWidget *widget,
  944.         gpointer   data)
  945. {
  946.   Resize *resize;
  947.   ResizePrivate *private;
  948.   gdouble width;
  949.   gdouble height;
  950.   gdouble ratio_x;
  951.   gdouble ratio_y;
  952.  
  953.   resize  = (Resize *) data;
  954.   private = (ResizePrivate *) resize->private_part;
  955.  
  956.   width  = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), 0);
  957.   height = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), 1);
  958.  
  959.   ratio_x = GTK_ADJUSTMENT (private->ratio_x_adj)->value;
  960.   ratio_y = GTK_ADJUSTMENT (private->ratio_y_adj)->value;
  961.  
  962.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (private->constrain)))
  963.     {
  964.       if (ratio_x != resize->ratio_x)
  965.     {
  966.       ratio_y = ratio_x;
  967.     }
  968.       else
  969.     {
  970.       ratio_x = ratio_y;
  971.     }
  972.     }
  973.  
  974.   width  = CLAMP (private->old_width * ratio_x,
  975.           GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE);
  976.   height = CLAMP (private->old_height * ratio_y,
  977.           GIMP_MIN_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE);
  978.  
  979.   size_update (resize, width, height, ratio_x, ratio_y);
  980. }
  981.  
  982. static void
  983. size_update (Resize *resize,
  984.          double  width,
  985.          double  height,
  986.          double  ratio_x,
  987.          double  ratio_y)
  988. {
  989.   ResizePrivate *private;
  990.  
  991.   private = (ResizePrivate *) resize->private_part;
  992.  
  993.   resize->width = (gint) (width + 0.5);
  994.   resize->height = (gint) (height + 0.5);
  995.  
  996.   resize->ratio_x = ratio_x;
  997.   resize->ratio_y = ratio_y;
  998.  
  999.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->size_se), resize);
  1000.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se),
  1001.                   0, width);
  1002.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se),
  1003.                   1, height);
  1004.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->size_se), resize);
  1005.  
  1006.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->ratio_x_adj), resize);
  1007.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->ratio_y_adj), resize);
  1008.   gtk_adjustment_set_value (GTK_ADJUSTMENT (private->ratio_x_adj), ratio_x);
  1009.   gtk_adjustment_set_value (GTK_ADJUSTMENT (private->ratio_y_adj), ratio_y);
  1010.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->ratio_x_adj), resize);
  1011.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->ratio_y_adj), resize);
  1012.  
  1013.   if (resize->type == ResizeWidget)
  1014.     {
  1015.       resize->offset_x = resize_bound_off_x (resize, resize->offset_x);
  1016.       resize->offset_y = resize_bound_off_y (resize, resize->offset_y);
  1017.  
  1018.       gimp_size_entry_set_refval_boundaries
  1019.     (GIMP_SIZE_ENTRY (private->offset_se), 0,
  1020.      MIN (0, resize->width - private->old_width),
  1021.      MAX (0, resize->width - private->old_width));
  1022.       gimp_size_entry_set_refval_boundaries
  1023.     (GIMP_SIZE_ENTRY (private->offset_se), 1,
  1024.      MIN (0, resize->height - private->old_height),
  1025.      MAX (0, resize->height - private->old_height));
  1026.  
  1027.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se),
  1028.                   0, resize->offset_x);
  1029.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se),
  1030.                   1, resize->offset_y);
  1031.     }
  1032.  
  1033.   if ((resize->type == ScaleWidget) && (resize->target == ResizeImage))
  1034.     {
  1035.       gtk_signal_handler_block_by_data (GTK_OBJECT (private->printsize_se),
  1036.                     resize);
  1037.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->printsize_se),
  1038.                   0, width);
  1039.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->printsize_se),
  1040.                   1, height);
  1041.       gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->printsize_se),
  1042.                       resize);
  1043.     }
  1044.  
  1045.   resize_draw (resize);
  1046. }
  1047.  
  1048. static void
  1049. printsize_update (GtkWidget *widget,
  1050.           gpointer   data)
  1051. {
  1052.   Resize *resize;
  1053.   ResizePrivate *private;
  1054.   gdouble width;
  1055.   gdouble height;
  1056.   gdouble print_width;
  1057.   gdouble print_height;
  1058.   gdouble res_x;
  1059.   gdouble res_y;
  1060.  
  1061.   resize  = (Resize *) data;
  1062.   private = (ResizePrivate *) resize->private_part;
  1063.  
  1064.   width  = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), 0);
  1065.   height = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->size_se), 1);
  1066.  
  1067.   print_width =
  1068.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->printsize_se), 0);
  1069.   print_height =
  1070.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->printsize_se), 1);
  1071.  
  1072.   /*  this is tricky: we use the sizes in pixels (which is otherwise
  1073.    *  meaningless for the "print size" widgets) to calculate the new
  1074.    *  resolution.
  1075.    */
  1076.   res_x = CLAMP (resize->resolution_x * width / print_width,
  1077.          GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION);
  1078.   res_y = CLAMP (resize->resolution_y * height / print_height,
  1079.          GIMP_MIN_RESOLUTION, GIMP_MAX_RESOLUTION);
  1080.  
  1081.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (private->equal_res)))
  1082.     {
  1083.       if (res_x != resize->resolution_x)
  1084.     {
  1085.       res_y = res_x;
  1086.     }
  1087.       else
  1088.     {
  1089.       res_x = res_y;
  1090.     }
  1091.     }
  1092.  
  1093.   resize->resolution_x = res_x;
  1094.   resize->resolution_y = res_y;
  1095.  
  1096.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->resolution_se), resize);
  1097.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->resolution_se),
  1098.                   0, res_x);
  1099.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->resolution_se),
  1100.                   1, res_y);
  1101.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->resolution_se),
  1102.                       resize);
  1103.  
  1104.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->size_se), resize);
  1105.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se),
  1106.                   0, res_x, TRUE);
  1107.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se),
  1108.                   1, res_y, TRUE);
  1109.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se),
  1110.                   0, width);
  1111.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->size_se),
  1112.                   1, height);
  1113.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->size_se), resize);
  1114.  
  1115.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->printsize_se), resize);
  1116.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->printsize_se),
  1117.                   0, res_x, TRUE);
  1118.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->printsize_se),
  1119.                   1, res_y, TRUE);
  1120.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->printsize_se),
  1121.                   0, width);
  1122.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->printsize_se),
  1123.                   1, height);
  1124.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->printsize_se),
  1125.                       resize);
  1126. }
  1127.  
  1128. /* Callback for resolution change. */
  1129. static void
  1130. resolution_callback (GtkWidget *widget,
  1131.              gpointer   data)
  1132. {
  1133.   Resize *resize;
  1134.   ResizePrivate *private;
  1135.   gdouble res_x;
  1136.   gdouble res_y;
  1137.   
  1138.   resize  = (Resize *) data;
  1139.   private = (ResizePrivate *) resize->private_part;
  1140.  
  1141.   res_x =
  1142.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->resolution_se), 0);
  1143.   res_y =
  1144.     gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (private->resolution_se), 1);
  1145.  
  1146.   if (gimp_chain_button_get_active (GIMP_CHAIN_BUTTON (private->equal_res)))
  1147.     {
  1148.       if (res_x != resize->resolution_x)
  1149.     {
  1150.       res_y = res_x;
  1151.     }
  1152.       else
  1153.     {
  1154.       res_x = res_y;
  1155.     }
  1156.     }
  1157.  
  1158.   resolution_update (resize, res_x, res_y);
  1159. }
  1160.  
  1161. /* Update widgets with resolution settings found in given Resize struct. */
  1162. static void
  1163. resolution_update (Resize  *resize,
  1164.            gdouble  res_x,
  1165.            gdouble  res_y)
  1166. {
  1167.   ResizePrivate *private;
  1168.  
  1169.   private = (ResizePrivate *) resize->private_part;
  1170.  
  1171.   resize->resolution_x = res_x;
  1172.   resize->resolution_y = res_y;
  1173.  
  1174.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->resolution_se), resize);
  1175.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->resolution_se),
  1176.                   0, res_x);
  1177.   gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->resolution_se),
  1178.                   1, res_y);
  1179.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->resolution_se),
  1180.                       resize);
  1181.  
  1182.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->size_se), resize);
  1183.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se),
  1184.                   0, res_x, TRUE);
  1185.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->size_se),
  1186.                   1, res_y, TRUE);
  1187.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->size_se), resize);
  1188.  
  1189.   gtk_signal_handler_block_by_data (GTK_OBJECT (private->printsize_se), resize);
  1190.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->printsize_se),
  1191.                   0, res_x, TRUE);
  1192.   gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (private->printsize_se),
  1193.                   1, res_y, TRUE);
  1194.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (private->printsize_se),
  1195.                       resize);
  1196. }
  1197.  
  1198. static gint
  1199. resize_events (GtkWidget *widget,
  1200.            GdkEvent  *event)
  1201. {
  1202.   Resize *resize;
  1203.   ResizePrivate *private;
  1204.   gint dx, dy;
  1205.   gint off_x, off_y;
  1206.  
  1207.   resize  = (Resize *) gtk_object_get_user_data (GTK_OBJECT (widget));
  1208.   private = (ResizePrivate *) resize->private_part;
  1209.  
  1210.   switch (event->type)
  1211.     {
  1212.     case GDK_EXPOSE:
  1213.       resize_draw (resize);
  1214.       break;
  1215.     case GDK_BUTTON_PRESS:
  1216.       gdk_pointer_grab (private->drawing_area->window, FALSE,
  1217.             (GDK_BUTTON1_MOTION_MASK |
  1218.              GDK_BUTTON_RELEASE_MASK),
  1219.             NULL, NULL, event->button.time);
  1220.       private->orig_x  = resize->offset_x;
  1221.       private->orig_y  = resize->offset_y;
  1222.       private->start_x = event->button.x;
  1223.       private->start_y = event->button.y;
  1224.       break;
  1225.     case GDK_MOTION_NOTIFY:
  1226.       /*  X offset  */
  1227.       dx = event->motion.x - private->start_x;
  1228.       off_x = private->orig_x + dx / private->ratio;
  1229.       off_x = resize_bound_off_x (resize, off_x);
  1230.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se),
  1231.                   0, off_x);
  1232.  
  1233.       /*  Y offset  */
  1234.       dy = event->motion.y - private->start_y;
  1235.       off_y = private->orig_y + dy / private->ratio;
  1236.       off_y = resize_bound_off_y (resize, off_y);
  1237.       gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (private->offset_se),
  1238.                   1, off_y);
  1239.  
  1240.       gtk_signal_emit_by_name (GTK_OBJECT (private->offset_se), "value_changed",
  1241.                    resize);
  1242.       break;
  1243.     case GDK_BUTTON_RELEASE:
  1244.       gdk_pointer_ungrab (event->button.time);
  1245.       break;
  1246.     default:
  1247.       break;
  1248.     }
  1249.  
  1250.   return FALSE;
  1251. }
  1252.  
  1253. /*** Resize sanity checks ***/
  1254.  
  1255. void
  1256. resize_scale_implement (ImageResize *image_scale)
  1257. {
  1258.   GImage   *gimage        = NULL;
  1259.   gboolean  rulers_flush  = FALSE;
  1260.   gboolean  display_flush = FALSE;  /* this is a bit ugly: 
  1261.                        we hijack the flush variable 
  1262.                        to check if an undo_group was 
  1263.                        already started */
  1264.  
  1265.   g_assert (image_scale != NULL);
  1266.   gimage = image_scale->gimage;
  1267.   g_assert (gimage != NULL);
  1268.  
  1269.   if (image_scale->resize->resolution_x != gimage->xresolution ||
  1270.       image_scale->resize->resolution_y != gimage->yresolution)
  1271.     {
  1272.       undo_push_group_start (gimage, IMAGE_SCALE_UNDO);
  1273.       
  1274.       gimage_set_resolution (gimage,
  1275.                  image_scale->resize->resolution_x,
  1276.                  image_scale->resize->resolution_y);
  1277.  
  1278.       rulers_flush = TRUE;
  1279.       display_flush = TRUE;
  1280.     }
  1281.  
  1282.   if (image_scale->resize->unit != gimage->unit)
  1283.     {
  1284.       if (!display_flush)
  1285.     undo_push_group_start (gimage, IMAGE_SCALE_UNDO);
  1286.  
  1287.       gimage_set_unit (gimage, image_scale->resize->unit);
  1288.       gdisplays_setup_scale (gimage);
  1289.       gdisplays_resize_cursor_label (gimage);
  1290.  
  1291.       rulers_flush = TRUE;
  1292.       display_flush = TRUE;
  1293.     }
  1294.  
  1295.   if (image_scale->resize->width != gimage->width ||
  1296.       image_scale->resize->height != gimage->height)
  1297.     {
  1298.       if (image_scale->resize->width > 0 &&
  1299.       image_scale->resize->height > 0) 
  1300.     {
  1301.       if (!display_flush)
  1302.         undo_push_group_start (gimage, IMAGE_SCALE_UNDO);
  1303.  
  1304.       gimage_scale (gimage,
  1305.             image_scale->resize->width,
  1306.             image_scale->resize->height);
  1307.  
  1308.       display_flush = TRUE;
  1309.     }
  1310.       else
  1311.     {
  1312.       g_message (_("Scale Error: Both width and height must be "
  1313.                "greater than zero."));
  1314.       return;
  1315.     }
  1316.     }
  1317.  
  1318.   if (rulers_flush)
  1319.     {
  1320.       gdisplays_setup_scale (gimage);
  1321.       gdisplays_resize_cursor_label (gimage);
  1322.     }
  1323.       
  1324.   if (display_flush)
  1325.     {
  1326.       undo_push_group_end (gimage);
  1327.       gdisplays_flush ();
  1328.     }
  1329. }
  1330.  
  1331. static void
  1332. resize_scale_warn_callback (GtkWidget *widget,
  1333.                 gboolean   do_scale,
  1334.                 gpointer   data)
  1335. {
  1336.   ImageResize *image_scale = NULL;
  1337.   GImage      *gimage      = NULL;
  1338.  
  1339.   g_assert (data != NULL);
  1340.   image_scale = (ImageResize *) data;
  1341.   gimage      = image_scale->gimage;
  1342.   g_assert (gimage != NULL);
  1343.  
  1344.   if (do_scale == TRUE) /* User doesn't mind losing layers... */
  1345.     {
  1346.       resize_scale_implement (image_scale);
  1347.       gtk_widget_destroy (image_scale->resize->resize_shell);
  1348.     }
  1349.   else
  1350.     {
  1351.       gtk_widget_set_sensitive (image_scale->resize->resize_shell, TRUE);
  1352.     }
  1353. }
  1354.  
  1355. gboolean 
  1356. resize_check_layer_scaling (ImageResize *image_scale)
  1357. {
  1358.   /* Inventory the layer list in gimage and return TRUE if, after
  1359.    * scaling, they all retain positive x and y pixel dimensions.
  1360.    * Otherwise, put up a modal boolean dialog box and ask the user if
  1361.    * she wishes to proceed. Return FALSE in the dialog case; the dialog box
  1362.    * callback will complete the job if the user really wants to
  1363.    * proceed. <02/22/2000 gosgood@idt.net>
  1364.    */
  1365.  
  1366.   gboolean   success = FALSE;
  1367.   GImage    *gimage  = NULL;
  1368.   GSList    *list    = NULL;
  1369.   Layer     *layer   = NULL;
  1370.   GtkWidget *dialog  = NULL;
  1371.  
  1372.   g_assert (image_scale != NULL);
  1373.  
  1374.   if (NULL != (gimage = image_scale->gimage))
  1375.     {
  1376.       /* Step through layers; test scaled dimensions. */
  1377.  
  1378.       success = TRUE;
  1379.       list    = gimage->layers;
  1380.       while (list && success == TRUE)
  1381.     {
  1382.       layer   = (Layer *)list->data;
  1383.       success = layer_check_scaling (layer, 
  1384.                      image_scale->resize->width,
  1385.                      image_scale->resize->height);
  1386.       list   = g_slist_next (list);
  1387.       
  1388.     }
  1389.       /* Warn user on failure */
  1390.       if (success == FALSE)
  1391.     {
  1392.       dialog =
  1393.         gimp_query_boolean_box (_("Layer Too Small"),
  1394.                     gimp_standard_help_func,
  1395.                     "dialogs/scale_layer_warn.html",
  1396.                     FALSE,
  1397.                     _("The chosen image size will shrink\n"
  1398.                       "some layers completely away.\n"
  1399.                       "Is this what you want?"),
  1400.                     _("OK"), _("Cancel"),
  1401.                     GTK_OBJECT (image_scale->resize->resize_shell),
  1402.                     "destroy",
  1403.                     resize_scale_warn_callback,
  1404.                     image_scale);
  1405.       gtk_widget_show (dialog);
  1406.     }
  1407.     }
  1408.   return success;
  1409. }
  1410.